/*-------------------------------------------------------------------------
 *
 * heaptoast.h
 *	  针对可变大小属性的外部和压缩存储的堆特定定义
 *
 * Copyright (c) 2000-2022, PostgreSQL Global Development Group
 *
 * src/include/access/heaptoast.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef HEAPTOAST_H
#define HEAPTOAST_H

#include "access/htup_details.h"
#include "storage/lockdefs.h"
#include "utils/relcache.h"

/*
 * 如果每页要有 N 个元组，则找到元组的最大大小。
 */
#define MaximumBytesPerTuple(tuplesPerPage) \
	MAXALIGN_DOWN((BLCKSZ - \
				   MAXALIGN(SizeOfPageHeaderData + (tuplesPerPage) * sizeof(ItemIdData))) \
				  / (tuplesPerPage))

/*
 * 这些符号控制 toaster 激活。如果一个元组大于
 * TOAST_TUPLE_THRESHOLD，我们将尝试通过压缩可压缩字段
 * 并将 EXTENDED 和 EXTERNAL 数据移出行来将其压缩到不超过
 * TOAST_TUPLE_TARGET 字节。
 *
 * 这些数字不必相同，尽管它们目前是。对于 TARGET 超过 THRESHOLD
 * 是没有意义的，但将其设置得更小可能是有用的。
 *
 * 目前我们选择两个值以匹配可以在堆页面上容纳的最大元组大小
 * TOAST_TUPLES_PER_PAGE。
 *
 * XXX 尽管这些可以在不调用 initdb 的情况下进行修改，但在
 * 随意更改之前需要考虑 toasting.c 中的 needs_toast_table()。
 * 另见 large_object.h 中的 LOBLKSIZE，该值 *不能* 在没有
 * initdb 的情况下更改。
 */
#define TOAST_TUPLES_PER_PAGE	4

#define TOAST_TUPLE_THRESHOLD	MaximumBytesPerTuple(TOAST_TUPLES_PER_PAGE)

#define TOAST_TUPLE_TARGET		TOAST_TUPLE_THRESHOLD

/*
 * 该代码还会考虑将 MAIN 数据移出行，但只有在前面的步骤
 * 没有达到目标元组大小时作为最后手段。在此阶段，我们使用
 * 另一个目标大小，当前等于可以放入堆页面的最大元组。
 * 这是合理的，因为用户已经告诉我们尽可能保持数据在行内。
 */
#define TOAST_TUPLES_PER_PAGE_MAIN	1

#define TOAST_TUPLE_TARGET_MAIN MaximumBytesPerTuple(TOAST_TUPLES_PER_PAGE_MAIN)

/*
 * 如果索引值大于 TOAST_INDEX_TARGET，我们将尝试
 * 压缩它（但是我们无法将其移出行）。注意，这个数字是
 * 每个数据项，而不是每个元组，以便简化 index_form_tuple()。
 */
#define TOAST_INDEX_TARGET		(MaxHeapTupleSize / 16)

/*
 * 当我们将过大的数据项存储在外部时，我们将其分成包含
 * 最多 TOAST_MAX_CHUNK_SIZE 字节的数据块。这个数字 *必须*
 * 足够小，以便完成的 toast-table 元组（包括 ID 和序列字段
 * 以及所有开销）可以适应一页。
 * 此处的编码设置了大小，理论上我们想要将最大大小的
 * EXTERN_TUPLES_PER_PAGE 元组放入一页。
 *
 * 注意：改变 TOAST_MAX_CHUNK_SIZE 需要一次 initdb。
 */
#define EXTERN_TUPLES_PER_PAGE	4	/* 只调整这一点 */

#define EXTERN_TUPLE_MAX_SIZE	MaximumBytesPerTuple(EXTERN_TUPLES_PER_PAGE)

#define TOAST_MAX_CHUNK_SIZE	\
	(EXTERN_TUPLE_MAX_SIZE -							\
	 MAXALIGN(SizeofHeapTupleHeader) -					\
	 sizeof(Oid) -										\
	 sizeof(int32) -									\
	 VARHDRSZ)

/* ----------
 * heap_toast_insert_or_update -
 *
 *	由 heap_insert() 和 heap_update() 调用。
 * ----------
 */
extern HeapTuple heap_toast_insert_or_update(Relation rel, HeapTuple newtup,
											 HeapTuple oldtup, int options);

/* ----------
 * heap_toast_delete -
 *
 *	由 heap_delete() 调用。
 * ----------
 */
extern void heap_toast_delete(Relation rel, HeapTuple oldtup,
							  bool is_speculative);

/* ----------
 * toast_flatten_tuple -
 *
 *	“扁平化”一个元组以包含不带行外 toasted 字段。
 *	（这不会消除压缩或短头数据项。）
 * ----------
 */
extern HeapTuple toast_flatten_tuple(HeapTuple tup, TupleDesc tupleDesc);

/* ----------
 * toast_flatten_tuple_to_datum -
 *
 *	将包含行外 toasted 字段的元组“扁平化”为 Datum。
 * ----------
 */
extern Datum toast_flatten_tuple_to_datum(HeapTupleHeader tup,
										  uint32 tup_len,
										  TupleDesc tupleDesc);

/* ----------
 * toast_build_flattened_tuple -
 *
 *	构建一个不包含行外 toasted 字段的元组。
 *	（这不会消除压缩或短头数据项。）
 * ----------
 */
extern HeapTuple toast_build_flattened_tuple(TupleDesc tupleDesc,
											 Datum *values,
											 bool *isnull);

/* ----------
 * heap_fetch_toast_slice
 *
 *	从存储在堆表中的 toast 值中提取切片。
 * ----------
 */
extern void heap_fetch_toast_slice(Relation toastrel, Oid valueid,
								   int32 attrsize, int32 sliceoffset,
								   int32 slicelength, struct varlena *result);

#endif							/* HEAPTOAST_H */
